Skip to content

feat(miner-hands): compose an immutable per-attempt acceptance-criteria document - #4449

Merged
loopover-orb[bot] merged 1 commit into
JSONbored:mainfrom
jeffrey701:feat/miner-acceptance-criteria-v2
Jul 9, 2026
Merged

feat(miner-hands): compose an immutable per-attempt acceptance-criteria document#4449
loopover-orb[bot] merged 1 commit into
JSONbored:mainfrom
jeffrey701:feat/miner-acceptance-criteria-v2

Conversation

@jeffrey701

Copy link
Copy Markdown
Contributor

Closes #4271

What

Before a coding-agent driver (#4262's interface; #4266/#4267's implementations) starts editing, the miner needs to pin down — immutably, so the agent can't quietly redefine its own success bar mid-attempt — what "done" means for this attempt. This adds the pure composition step that folds the two already-shipped Phase 2 primitives into a single acceptance-criteria document:

  • the sanitized PromptPacket (prompt-packet.ts:26, the analyze→coding-agent "boundary membrane"), and
  • the FeasibilityGateResult go/raise/avoid verdict (feasibility.ts:21-26).

This is the direct input to preparePlanTemplate's coding-agent step (plan-templates.ts:84, which runs after branch-create and before local-test). Producing the document is this module's job; writing it into the attempt worktree (#4269) and handing it to the driver interface (#4262) are separate concerns, so this stays pure and side-effect-free like the rest of gittensory-engine.

Decisions (the issue's open questions, resolved here)

  • Serialization: JSON, not markdown. The criteria are an immutable, checksum-verifiable success bar consumed by tooling (a self-review step must be able to prove the target didn't move across iterations), so a deterministic canonically-ordered JSON document beats prose. serializeAcceptanceCriteria emits stable-key-order JSON with a trailing newline so a recorded checksum stays byte-stable across processes.
  • Filename: one fixed name, ACCEPTANCE_CRITERIA_FILENAME (acceptance-criteria.json).
  • Immutability as a real property: the built document is deep-frozen (Object.freeze, arrays copied+frozen) so it can't be mutated in-memory for the lifetime of the attempt; the byte-stable serialization is what a caller checksums on disk.
  • Written only on go: a raise/avoid verdict means the attempt should not start, so no criteria file is written. buildAcceptanceCriteria still returns a document with writable: false so a caller can log why it was skipped; shouldWriteAcceptanceCriteria is the single gate for the write itself.

Redaction is delegated to sanitizePromptPacketField rather than re-implemented, since this document is exactly as exposed to a prompt-injectable coding-agent session as the prompt packet already is (idempotent on already-sanitized text).

API

  • buildAcceptanceCriteria({ promptPacket, feasibility }) => AcceptanceCriteria — pure, deep-frozen.
  • serializeAcceptanceCriteria(doc) => string — deterministic canonical JSON + trailing newline.
  • shouldWriteAcceptanceCriteria(verdict) => boolean — the go-only write gate.
  • ACCEPTANCE_CRITERIA_FILENAME, ACCEPTANCE_CRITERIA_VERSION.

Files

  • packages/gittensory-engine/src/miner/acceptance-criteria.ts — the composer.
  • packages/gittensory-engine/src/index.ts — barrel re-export.
  • test/unit/miner-acceptance-criteria.test.ts — tests (composition from both inputs, redaction pass-through, raise/avoid non-writable cases, deep-freeze/immutability, deterministic serialization).

Testing

npx vitest run test/unit/miner-acceptance-criteria.test.ts
npm run typecheck

8/8 tests pass; typecheck clean. New engine file is fully covered (7/7 lines, 3/3 functions, no uncovered branches).

…ia document

Before a coding-agent driver starts editing, the miner needs to pin down —
immutably, so the agent cannot quietly redefine its own success bar mid-attempt
— what "done" means for this attempt. This adds the pure composition step that
folds the two already-shipped Phase 2 primitives into one document:

- the sanitized PromptPacket (prompt-packet.ts, the analyze→coding-agent
  boundary membrane), and
- the FeasibilityGateResult go/raise/avoid verdict (feasibility.ts).

Producing the document is this module's job; writing it into the attempt
worktree (JSONbored#4269) and handing it to the driver interface (JSONbored#4262) are separate
concerns, so this stays pure and side-effect-free like the rest of the engine.

Decisions made per the issue's open questions:
- Serialization is deterministic, canonically-ordered JSON (not markdown), so a
  checksum recorded alongside stays byte-stable — the success bar is a
  machine-verifiable target a self-review step must prove did not move.
- One fixed filename, ACCEPTANCE_CRITERIA_FILENAME.
- The built document is deep-frozen so it cannot be mutated in-memory for the
  lifetime of the attempt.
- Only a `go` verdict authorizes the attempt to start, so only `go` gets a file
  written (shouldWriteAcceptanceCriteria); a raise/avoid document is still
  returned with writable=false so a caller can log why it was skipped.

Redaction is delegated to sanitizePromptPacketField rather than re-implemented,
since this document is as exposed to a prompt-injectable session as the packet.

- packages/gittensory-engine/src/miner/acceptance-criteria.ts: the composer.
- packages/gittensory-engine/src/index.ts: barrel re-export of the API.
- test/unit/miner-acceptance-criteria.test.ts: full coverage.

Closes JSONbored#4271
@jeffrey701
jeffrey701 requested a review from JSONbored as a code owner July 9, 2026 19:09
@superagent-security

Copy link
Copy Markdown
Contributor

Superagent didn't find any vulnerabilities or security issues in this PR.

@loopover-orb loopover-orb Bot added the gittensor:feature Gittensor-scored feature linked to a feature issue — scores a 0.25x multiplier. label Jul 9, 2026
@codecov

codecov Bot commented Jul 9, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 93.97%. Comparing base (58e08e9) to head (0bcdd49).
⚠️ Report is 5 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #4449   +/-   ##
=======================================
  Coverage   93.97%   93.97%           
=======================================
  Files         415      416    +1     
  Lines       37335    37342    +7     
  Branches    13647    13647           
=======================================
+ Hits        35086    35093    +7     
  Misses       1594     1594           
  Partials      655      655           
Files with missing lines Coverage Δ
...gittensory-engine/src/miner/acceptance-criteria.ts 100.00% <100.00%> (ø)
🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@loopover-orb

loopover-orb Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Tip

🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩

✅ Gittensory review result - approve/merge recommended

Review updated: 2026-07-09 19:22:06 UTC

3 files · 1 AI reviewer · no blockers · readiness 100/100 · CI green · clean

✅ Suggested Action - Approve/Merge

  • safe to merge

Review summary
This adds a pure composition function that folds a sanitized PromptPacket and a FeasibilityGateResult into a single immutable, deterministically-serializable acceptance-criteria document, without performing any file I/O (writing is deliberately deferred to a separate worktree primitive per #4269). The logic is straightforward and correct: freezing is applied to both the top-level object and its array fields, gating is centralized in shouldWriteAcceptanceCriteria, and serialization uses a fixed key order independent of input key order. Tests cover the go/raise/avoid paths, redaction delegation, deep-freeze immutability, and serialization determinism, and the PR closes the linked issue #4271 with a narrowly-scoped, side-effect-free change consistent with the rest of gittensory-engine.

Nits — 5 non-blocking
  • acceptance-criteria.ts:3-27 — the top-of-file comment block is unusually long (25 lines) for a single-purpose module; consider trimming to the non-obvious decisions (JSON format choice, freeze rationale) and letting the JSDoc on each export carry the rest.
  • acceptance-criteria.ts:44 — `version: number` could be narrowed to the literal type `1` (or `typeof ACCEPTANCE_CRITERIA_VERSION`) so a future version bump is caught by the type checker at every construction site.
  • No test exercises `buildAcceptanceCriteria` with a verdict that has both empty avoidReasons and raiseReasons alongside a non-'go' verdict path other than avoid/raise (e.g., confirm 'raise' with multiple reasons preserves array order) — minor, since the array is just a spread copy.
  • Consider adding a round-trip test that JSON.parses `serializeAcceptanceCriteria` output and re-freezes it, confirming the on-disk shape is exactly what a future worktree-writer / self-review checksum step expects, since that's the stated motivation for the JSON format decision.
  • The long block comment (acceptance-criteria.ts:3-27) duplicates most of the PR description; moving the rationale into the PR/issue and keeping the file comment to a couple of lines would reduce future drift risk if the design evolves.
Signal Result Evidence
Code review ✅ No blockers 1 reviewer
Linked issue ✅ Linked #4271
Related work ✅ No active overlap found No same-issue or scoped active PR overlap found.
Change scope ✅ 20/20 Low review scope from cached public metadata (1 linked issue).
Validation posture ✅ 25/25 PR body includes validation/test evidence.
Contributor workload ✅ 10/10 Author activity: 97 registered-repo PR(s), 40 merged, 4 issue(s).
Contributor context ✅ Confirmed Gittensor contributor jeffrey701; Gittensor profile; 97 PR(s), 4 issue(s).
Gate result ✅ Passing No configured blocker found.
Linked issue satisfaction

Addressed
The PR adds acceptance-criteria.ts with a pure buildAcceptanceCriteria composer that folds PromptPacket and FeasibilityGateResult via sanitizePromptPacketField, defines a fixed JSON filename and version, deep-freezes the document for immutability, and gates writability on the go verdict while still returning raise/avoid documents with writable:false — matching all four deliverables including the e

Review context
  • Author: jeffrey701
  • Role context: outside_contributor
  • Public audience mode: oss maintainer
  • Lane context: Repository is configured for direct PR review.
  • Public profile languages: TypeScript, Clojure, JavaScript
  • Official Gittensor activity: 97 PR(s), 4 issue(s).
  • PR-specific overlap: none found.
Contributor next steps
  • Keep the PR focused and include validation evidence before maintainer review.
Signal definitions
  • Related work = same linked issue, overlapping active PRs, or title/path similarity.
  • Change scope = cached public metadata such as size labels, draft state, and review-burden hints.
  • Validation posture = whether the PR provides enough public validation/test evidence for maintainer review.
  • Contributor workload = public contributor activity and cleanup pressure, not a repo-wide quality failure.
  • Contributor context = public GitHub/Gittensor identity context; non-Gittensor status is not a blocker.

🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed


💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →.

Checked by Gittensory, a quiet PR intelligence layer for OSS maintainers.

  • Re-run Gittensory review

@loopover-orb loopover-orb Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Gittensory approves — the gate is satisfied and CI is green.

@loopover-orb
loopover-orb Bot merged commit 861e8b7 into JSONbored:main Jul 9, 2026
10 checks passed
@loopover-orb loopover-orb Bot added gittensor:bug Gittensor-scored bug fix — scores a 0.05x multiplier. and removed gittensor:feature Gittensor-scored feature linked to a feature issue — scores a 0.25x multiplier. labels Jul 9, 2026
@JSONbored JSONbored added gittensor:feature Gittensor-scored feature linked to a feature issue — scores a 0.25x multiplier. and removed gittensor:bug Gittensor-scored bug fix — scores a 0.05x multiplier. labels Jul 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gittensor:feature Gittensor-scored feature linked to a feature issue — scores a 0.25x multiplier.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(miner-hands): immutable acceptance-criteria file written before the coding agent starts

2 participants